package de.medieninf.sensimg;

import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Graphics;

/**
 * A simple graphical visualization of a keypad for touch devices.
 * @author pb
 */
public class Keypad extends Canvas {
    /**
     * Number of columns of the keyboard.
     */
    static final int KP_COLUMNS = 3;
    /**
     * Number of rows of the keyboard.
     */
    static final int KP_ROWS = 4;
    /**
     * Margin in visualization of buttons.
     */
    static final int MARGIN = 2;
    /**
     * Rounded rectangle, size of the "rounded".
     */
    static final int RRECT = 4;
    /**
     * The characters visualized on the keypad.
     */
    static final char[][] KEYPAD_CHARS = {
        {'1', '2', '3'},
        {'4', '5', '6'},
        {'7', '8', '9'},
        {'*', '0', '#'}
    };

    /**
     * The width of the canvas.
     */
    private int width;
    /**
     * The height of the canvas.
     */
    private int height;
    /**
     * How much is the offset (y) of the font.
     */
    private int fontOffset = 0;
    /**
     * Remember whether the environment supports
     * pointerEvents (touch screen).
     */
    private boolean pointerEvents = false;
    /**
     * Which characters are currently being pressed.
     */
    private boolean[][] keypadPressed = {
        {false, false, false},
        {false, false, false},
        {false, false, false},
        {false, false, false},
    };
    /**
     * Local copy of reference to keyboardState.
     */
    private KeyboardState keyboardState;

    /**
     * Construct Keypad instance.
     * @param upperCl upper CommandListener
     * @param cmds commands of upper CommandListeners
     * @param pkeyboardState one keyboardState
     */
    public Keypad(final CommandListener upperCl, final Command[] cmds,
            final KeyboardState pkeyboardState) {
        this.keyboardState = pkeyboardState;
        for (int i = 0; i < cmds.length; i++) {
            addCommand(cmds[i]);
        }
        setCommandListener(upperCl);
    }

    /**
     * @param g Graphics to paint on
     */
    protected final void paint(final Graphics g) {
        // draw the keypad square
        int x = 0;
        int y = 0;
        if (fontOffset == 0) {
            fontOffset = g.getFont().getHeight() / 2;
        }
        g.setColor(Colors.WHITE);
        g.fillRect(0, 0, width, height);
        g.setColor(Colors.BLACK);
        if (!pointerEvents) {
            g.drawString("No pointer events",
                         width / 2, (height / 2) - (2 * fontOffset),
                         Graphics.BASELINE | Graphics.HCENTER);
            g.drawString("Go Back, use keyboard",
                         width / 2, (height / 2) + (2 * fontOffset),
                         Graphics.BASELINE | Graphics.HCENTER);
            return;
        }
        // for the 4x3 keypad area
        for (int i = 0; i < KP_ROWS; i++) {
            x = 0;
            for (int j = 0; j < KP_COLUMNS; j++) {
                g.setColor(Colors.BLACK);
                g.drawRoundRect(x + MARGIN, y + MARGIN,
                                (width / KP_COLUMNS) - (2 * MARGIN),
                                (height / KP_ROWS) - (2 * MARGIN),
                                 RRECT, RRECT);
                if (keypadPressed[i][j]) { // fill orange if pressed
                    g.setColor(Colors.ORANGE);
                    g.fillRoundRect(x + MARGIN + 1, y + MARGIN + 1,
                                (width / KP_COLUMNS) - (2 * MARGIN) + MARGIN,
                                (height / KP_ROWS) - (2 * MARGIN) - MARGIN,
                                 RRECT, RRECT);
                }
                g.setColor(Colors.BLACK);
                g.drawChar(KEYPAD_CHARS[i][j],
                           x + (width / (2 * KP_COLUMNS)),
                           y + (height / (2 * KP_ROWS)) + fontOffset,
                           Graphics.HCENTER | Graphics.BASELINE);
                x += width / KP_COLUMNS;
            }
            y += height / KP_ROWS;
        }

    }

    /**
     * update dimensional and info data of canvas on showing.
     */
    protected final void showNotify() {
        super.showNotify();
        width = getWidth();
        height = getHeight();
        fontOffset = 0;
        pointerEvents = hasPointerEvents();
    }

    /**
     * Sets or unsets at specific logical position.
     * @param x the row
     * @param y the columns
     * @param state true iff setting otherwise false
     */
    private void setAt(final int x, final int y, final boolean state) {
        int j = x / (width / KP_COLUMNS);
        int i = y / (height / KP_ROWS);
        int ch = KEYPAD_CHARS[i][j];
        keypadPressed[i][j] = state;
        // Actually, if one drags out this can simulate constant pressing
        if (state) {
            keyboardState.onKeyDown(ch);
        } else {
            keyboardState.onKeyUp(ch);
        }
        repaint();
    }

    /**
     * Connect pointerPressed events.
     * @param x int, the x position on the screen
     * @param y int, the y position on the screen
     */
    protected final void pointerPressed(final int x, final int y) {
        setAt(x, y, true);
        super.pointerPressed(x, y);
    }

    /**
     * Connect pointerReleased events.
     * @param x int, the x position on the screen
     * @param y int, the y position on the screen
     */
    protected final void pointerReleased(final int x, final int y) {
        setAt(x, y, false);
        super.pointerReleased(x, y);
    }

    /**
     * Connect keyPress events.
     * @param keyCode the keyCode
     */
    protected final void keyPressed(final int keyCode) {
        keyboardState.onKeyDown(keyCode, getGameAction(keyCode));
        super.keyPressed(keyCode);
    }

    /**
     * Connect keyReleased events.
     * @param keyCode the keyCode
     */
    protected final void keyReleased(final int keyCode) {
        keyboardState.onKeyUp(keyCode, getGameAction(keyCode));
        super.keyReleased(keyCode);
    }
}
